﻿using System;
using System.Text;
using System.Threading;
using Microsoft.SPOT;

using mfRCF.Core.Extensions;

namespace mfRCF.Core
{
    public class mfRcf
    {
        #region Consts

        private const int C_INT_THREAD_STOP_DELAY = 500;

        #endregion

        #region CTOR// Singleton
        //http://msdn.microsoft.com/en-us/library/ff650316.aspx
        //--> not thread-safe, with static initialization

        private static readonly mfRcf _instance = new mfRcf();

        private mfRcf()
        {
            Commands = new mfRcfCommandCollection();
        }

        public static mfRcf Instance
        {
            get
            {
                return _instance;
            }
        }

        #endregion

        #region Fields

        mfIRcfPlattformProvider _plattformProvider = null;

        #endregion

        #region Properties

        public mfIRcfPlattformProvider PlattformProvider
        {
            get
            {
                return _plattformProvider;
            }
            set
            {
                AssertIfPlattformProviderIsInvalid(value);

                _plattformProvider = value;
            }
        }

        public mfRcfCommandCollection Commands
        {
            get;
            private set;
        }

        private Thread HttpThread
        {
            get;
            set;
        }

        private bool HttpThreadStopRequested
        {
            get;
            set;
        }

        #endregion

        #region Methods

        private static void AssertIfPlattformProviderIsInvalid(mfIRcfPlattformProvider value)
        {
            if (value.HttpListener == null
                || value.Authenticator == null)
            {
                throw new mfRcfException("specified plattform-provider not ready for register on rcf");
            }
        }

        public void Start()
        {
            AssertIfPlattformProviderIsInvalid(PlattformProvider);

            PlattformProvider.HttpListener.Start();
            HttpThreadStopRequested = false;

            HttpThread = new Thread(new ThreadStart(HandleHttpRequestsAsync));
            HttpThread.Start();
        }

        public void Stop()
        {
            HttpThreadStopRequested = true;
            Thread.Sleep(C_INT_THREAD_STOP_DELAY);

            HttpThreadAbort();

            PlattformProvider.HttpListener.Stop();

            HttpThread = null;
        }

        private void HttpThreadAbort()
        {
            if (HttpThread.IsAlive)
            {
                try
                {
                    HttpThread.Abort();
                }
                catch (Exception ex)
                {
                    Debug.Print("mfRcf.Stop error occured on httpThread.Abort(): " + ex.Message);
                }
            }
        }

        /// <summary>
        /// handles the incoming http requests and maps them on the registered commands
        /// </summary>
        /// <remarks>
        /// http errorhandling references2: https://www.ghielectronics.com/community/forum/topic?id=10275&page=1
        /// Hint: SocketExceptions with ErrorCode 10053 (ConnectionAborted) are ignored, they activated by the browsers:
        /// --> references2: http://netmf.codeplex.com/workitem/313
        /// </remarks>
        private void HandleHttpRequestsAsync()
        {
            var httpListener = PlattformProvider.HttpListener;

            mfIHttpContext context = null;
            object sync = new object();
            object syncBusy = new object();


            while (!HttpThreadStopRequested)
            {
                lock (sync)
                {
                    try
                    {
                        context = httpListener.Context;
                        if (context == null)
                        {
                            continue;
                        }

                        lock (syncBusy)
                        {
                            var requestUrl = context.Request.RawUrl.ToLower().ExtractBaseUrl();
                            var httpMethod = context.Request.HttpMethod.ToUpper();
                            var cmdExecuted = false;

                            foreach (mfRcfCommand cmd in Commands)
                            {
                                if (cmd.HttpPath.Equals(requestUrl)
                                    && cmd.HttpMethod.MethodName.Equals(httpMethod))
                                {
                                    cmd.ExecuteCommand(context);
                                    cmdExecuted = true;
                                    continue;
                                }
                            }

                            if (cmdExecuted)
                                continue;

                            var msg = "requested rcf-command " + requestUrl + " with method " + httpMethod + " does not exist";

                            Debug.Print(msg);
                            context.Response.StatusCode = enumHttpStatusCode.NotImplemented;
                            context.Response.OutputStream.WriteString(msg);
                            context.Response.Close(); 
                        }
                    }
                    catch (InvalidOperationException invalidOpEx)
                    {
                        Debug.Print("InvalidOperationException in HandleHttpRequestsAsync: " + invalidOpEx.Message + " stopping listener");

                        httpListener.Stop();
                        Thread.Sleep(C_INT_THREAD_STOP_DELAY);
                        return;
                    }
                    catch (ObjectDisposedException dispEx)
                    {
                        Debug.Print("ObjectDisposedException in HandleHttpRequestsAsync: " + dispEx.Message + " starting listener");

                        httpListener.Start();
                    }
                    catch (NotImplementedException ImplEx)
                    {
                        Debug.Print("not implemented exception in HandleHttpRequestsAsync: " + ImplEx.Message);

                        context.Response.StatusCode = enumHttpStatusCode.NotImplemented;
                        context.Response.OutputStream.WriteString(ImplEx.Message);
                        context.Response.Close();
                    }
                    catch (mfRcfException rcfEx)
                    {
                        Debug.Print("rcf exception in HandleHttpRequestsAsync: " + rcfEx.Message);

                        context.Response.StatusCode = enumHttpStatusCode.InternalServerError;
                        context.Response.OutputStream.WriteString(rcfEx.Message);
                        context.Response.Close();
                    }
                    catch (Exception ex)
                    {
                        Debug.Print("exception in HandleHttpRequestsAsync: " + ex.Message);
                    }
                }
            }

        }

        #endregion
    }
}
